#include "SoftIIC.h"


/** @brief Initialize the IIC configuration */
void SoftIIC_Init(const SoftIIC_HardwareTypeDef * const hardware) {
    SoftIIC_Hardware_Init(hardware);
    
    // Release IIC bus
    SoftIIC_SCL_High(hardware);
    SoftIIC_SDA_High(hardware);
}


/** @brief Send a IIC Start condition. */
void SoftIIC_Start(const SoftIIC_HardwareTypeDef * const hw) {
    // ready 
    SoftIIC_SCL_High(hw);
    SoftIIC_SDA_High(hw);
    delay_us(SOFT_IIC_TIM_STR_STP_DLY);
    
    // make Start condition and hold for a while
    SoftIIC_SDA_Low(hw);
    delay_us(SOFT_IIC_TIM_STR_STP_DLY); 
    
    // end this bit
    SoftIIC_SCL_Low(hw);
    delay_us(SOFT_IIC_TIM_CLK_LOW);
}


/** @brief Send a IIC Stop condition. */
void SoftIIC_Stop(const SoftIIC_HardwareTypeDef * hw) {
    // ready
    SoftIIC_SDA_Low(hw);
    delay_us(SOFT_IIC_TIM_DAT_DLY);
    
    SoftIIC_SCL_High(hw);
    delay_us(SOFT_IIC_TIM_STR_STP_DLY);
    
    // make Stop condition and hold for a while
    SoftIIC_SDA_High(hw);
    delay_us(SOFT_IIC_TIM_STR_STP_DLY);
}


/** @brief Send a IIC Ack bit. */
void SoftIIC_Ack(const SoftIIC_HardwareTypeDef * hw) {
    // ready
    SoftIIC_SDA_Low(hw);
    delay_us(SOFT_IIC_TIM_DAT_DLY);
    
    // write Ack bit
    SoftIIC_SCL_High(hw);
    delay_us(SOFT_IIC_TIM_CLK_HIGH);
    
    // end Ack bit
    SoftIIC_SCL_Low(hw);
    delay_us(SOFT_IIC_TIM_CLK_LOW);
}


/** @brief Wait the Ack bit from Slave. */
int SoftIIC_WaitAck(const SoftIIC_HardwareTypeDef * const hw) {
    int _wait_cnt;
    
    // release SDA
    SoftIIC_SDA_High(hw);
    delay_us(SOFT_IIC_TIM_DAT_DLY);
    
    // wait for Ack, or timeout
    SoftIIC_SCL_High(hw);
    for (_wait_cnt=0; _wait_cnt < 20; _wait_cnt++) {
        if (SoftIIC_LogicLOW == SoftIIC_SDA_Read(hw)) {
            break;
        }
    }
    
    // finish clock
    SoftIIC_SCL_Low(hw);
    delay_us(SOFT_IIC_TIM_CLK_LOW);
    
    if (_wait_cnt < 20) {
        return SOFT_IIC_ACK_OK;
    }
    else {
        return SOFT_IIC_ACK_ERR;
    }
}


void SoftIIC_SendByte(const SoftIIC_HardwareTypeDef * hw, unsigned char data) {
    for (int i=8; i>0; i--) {
        (data & 0x80) ? SoftIIC_SDA_High(hw) : SoftIIC_SDA_Low(hw);
        delay_us(SOFT_IIC_TIM_DAT_DLY);
        
        SoftIIC_SCL_High(hw);
        delay_us(SOFT_IIC_TIM_CLK_HIGH);
        
        SoftIIC_SCL_Low(hw);
        delay_us(SOFT_IIC_TIM_CLK_LOW);
        
        data = data << 1;
    }
}


unsigned char SoftIIC_ReadByte(const SoftIIC_HardwareTypeDef * hw) {
    unsigned char _data = 0;
    
    // release SDA line
    SoftIIC_SDA_High(hw);
    delay_us(SOFT_IIC_TIM_DAT_DLY);
    
    for (int i=0; i<8; i++) {
        // read bit
        SoftIIC_SCL_High(hw);
        delay_us(SOFT_IIC_TIM_CLK_HIGH);
        
        _data = _data << 1;
        _data = _data | SoftIIC_SDA_Read(hw);
        
        // wait next bit
        SoftIIC_SCL_Low(hw);
        delay_us(SOFT_IIC_TIM_CLK_LOW);
    }
    
    return _data;
}

